"""Property Matching Service - AI-enhanced property recommendation."""

from __future__ import annotations

import logging
import re
from typing import TYPE_CHECKING

from fastapi import HTTPException, status

from ..schemas.property import PropertyOut
from ..schemas.property_match import (
    MatchScoreBreakdown,
    PropertyMatchListResponse,
    PropertyMatchResult,
)
from ..schemas.ai_extraction import LeadRequirements, PropertyFeatures

if TYPE_CHECKING:
    from ..models.lead import Lead
    from ..models.property import Property
    from ..repositories.lead_repository import LeadRepository
    from ..repositories.property_repository import PropertyRepository
    from ..services.ai_extraction_service import AIExtractionService

logger = logging.getLogger(__name__)


class PropertyMatchService:
    """客户-房源智能匹配服务 (AI增强版)."""

    def __init__(
        self,
        lead_repo: LeadRepository,
        property_repo: PropertyRepository,
        ai_extraction: AIExtractionService,
    ):
        """
        初始化匹配服务.

        Args:
            lead_repo: 客户仓库
            property_repo: 房源仓库
            ai_extraction: AI提取服务
        """
        self.lead_repo = lead_repo
        self.property_repo = property_repo
        self.ai_extraction = ai_extraction

    def match_properties(
        self,
        lead_id: int,
        top_n: int = 10,
        min_score: float = 50.0,
        include_unavailable: bool = False,
    ) -> PropertyMatchListResponse:
        """
        为客户匹配房源 (AI增强).

        Args:
            lead_id: 客户ID
            top_n: 返回前N个匹配结果
            min_score: 最低匹配分数阈值
            include_unavailable: 是否包含已租出房源

        Returns:
            PropertyMatchListResponse: 匹配结果列表

        Raises:
            HTTPException: 客户不存在时抛出404
        """
        # 1. 获取客户信息
        lead = self.lead_repo.get_with_relations(lead_id)
        if not lead:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Lead not found")

        # 2. AI提取客户需求
        lead_req = self.ai_extraction.extract_lead_requirements(lead)

        # 3. 获取可用房源
        properties = self.property_repo.get_available_properties(
            include_unavailable=include_unavailable
        )

        # 4. 计算匹配分数 (AI增强 + 缓存)
        match_results = []
        for prop in properties:
            # 提取房源特征 (优先使用缓存)
            prop_features = self.ai_extraction.extract_property_features(prop)

            # 计算分数
            score_breakdown = self._calculate_match_score(
                lead=lead, property=prop, lead_req=lead_req, prop_features=prop_features
            )

            if score_breakdown.total_score >= min_score:
                match_results.append(
                    PropertyMatchResult(
                        property_id=prop.id,
                        property=PropertyOut.model_validate(prop),
                        match_score=score_breakdown.total_score,
                        score_breakdown=score_breakdown,
                        match_reasons=self._generate_match_reasons(
                            lead, prop, lead_req, prop_features, score_breakdown
                        ),
                        mismatch_reasons=self._generate_mismatch_reasons(
                            lead, prop, lead_req, prop_features, score_breakdown
                        ),
                        ai_insights=self._generate_ai_insights(lead_req, prop_features),
                    )
                )

        # 5. 排序并返回
        match_results.sort(key=lambda x: x.match_score, reverse=True)
        return PropertyMatchListResponse(
            lead_id=lead_id,
            total_matched=len(match_results),
            matches=match_results[:top_n],
        )

    def _calculate_match_score(
        self,
        lead: Lead,
        property: Property,
        lead_req: LeadRequirements,
        prop_features: PropertyFeatures,
    ) -> MatchScoreBreakdown:
        """
        计算匹配分数 (AI增强算法).

        Args:
            lead: 客户对象
            property: 房源对象
            lead_req: AI提取的客户需求
            prop_features: AI提取的房源特征

        Returns:
            MatchScoreBreakdown: 分数明细
        """
        # 1. 预算匹配 (35%)
        budget_score = self._calculate_budget_score(
            budget_min=lead_req.budget_min,
            budget_max=lead_req.budget_max,
            rent_price=property.rent_price,
            flexibility=lead_req.budget_flexibility,
        )

        # 2. 户型匹配 (25%)
        layout_score = self._calculate_layout_score(
            lead_layout=lead_req.layout,
            property_layout=property.layout,
        )

        # 3. 区域匹配 (20%) - AI增强语义匹配
        area_score = self._calculate_area_score_semantic(
            lead_areas=lead_req.area_preferences,
            commute_location=lead_req.commute_location,
            property_community=property.community_name,
            property_address=property.address,
            property_business_districts=prop_features.nearby_business_districts,
            property_landmarks=prop_features.nearby_landmarks,
        )

        # 4. 标签匹配 (15%) - 分优先级
        tag_score = self._calculate_tag_score_weighted(
            must_have=lead_req.must_have_tags,
            nice_to_have=lead_req.nice_to_have_tags,
            property_facilities=prop_features.facilities,
        )

        # 5. 通勤匹配 (5%) - AI独有
        commute_score = 0.0
        if lead_req.commute_location:
            commute_score = self._calculate_commute_score(
                lead_req=lead_req,
                prop_features=prop_features,
            )

        # 加权总分
        total_score = (
            budget_score * 0.35
            + layout_score * 0.25
            + area_score * 0.20
            + tag_score * 0.15
            + commute_score * 0.05
        )

        # 语义加分 (AI提供的额外洞察)
        semantic_bonus = 0.0
        if lead_req.extraction_method in ["ai", "hybrid"]:
            # AI提取的需求更准确,给予小幅加分
            semantic_bonus = 2.0

        total_score = min(100.0, total_score + semantic_bonus)

        return MatchScoreBreakdown(
            budget_score=budget_score,
            layout_score=layout_score,
            area_score=area_score,
            tag_score=tag_score,
            commute_score=commute_score,
            semantic_bonus=semantic_bonus,
            total_score=total_score,
        )

    def _calculate_budget_score(
        self,
        budget_min: int | None,
        budget_max: int | None,
        rent_price: int,
        flexibility: str,
    ) -> float:
        """计算预算匹配分数."""
        if not budget_min or not budget_max:
            return 50.0  # 预算不明确,给中等分

        budget_mid = (budget_min + budget_max) / 2
        budget_range_width = budget_max - budget_min

        # 房价在预算范围内
        if budget_min <= rent_price <= budget_max:
            return 100.0

        # 房价超出预算
        deviation = abs(rent_price - budget_mid)
        score = max(0, 100 - (deviation / budget_range_width) * 100)

        # 根据灵活度调整
        if flexibility == "flexible":
            score = min(100, score * 1.2)  # 灵活预算,提升20%
        elif flexibility == "strict":
            score = score * 0.8  # 严格预算,降低20%

        return score

    def _calculate_layout_score(
        self,
        lead_layout: str | None,
        property_layout: str,
    ) -> float:
        """计算户型匹配分数."""
        if not lead_layout:
            return 70.0  # 无户型要求,给中等偏上分

        # 完全匹配
        if lead_layout == property_layout:
            return 100.0

        # 提取数字进行相似匹配 (如 "2室1厅" vs "2室2厅")
        lead_rooms = self._extract_room_count(lead_layout)
        prop_rooms = self._extract_room_count(property_layout)

        if lead_rooms and prop_rooms:
            if lead_rooms == prop_rooms:
                return 85.0  # 室数相同但厅数不同
            elif abs(lead_rooms - prop_rooms) == 1:
                return 60.0  # 室数相差1

        return 0.0

    def _extract_room_count(self, layout: str) -> int | None:
        """从户型中提取室数."""
        match = re.search(r"(\d+)[室房]", layout)
        if match:
            return int(match.group(1))
        return None

    def _calculate_area_score_semantic(
        self,
        lead_areas: list[str],
        commute_location: str | None,
        property_community: str,
        property_address: str,
        property_business_districts: list[str],
        property_landmarks: list[str],
    ) -> float:
        """区域匹配分数 (AI增强语义相似度)."""
        if not lead_areas and not commute_location:
            return 70.0  # 无区域要求

        scores = []

        # 1. 直接区域匹配 (规则引擎)
        for area in lead_areas:
            # 完全匹配
            if area in property_community or area in property_address:
                scores.append(100.0)
                continue

            # 地标匹配
            for landmark in property_landmarks:
                if area in landmark:
                    scores.append(90.0)
                    break

            # 语义相似度 (AI增强)
            for landmark in property_landmarks:
                similarity = self.ai_extraction.semantic_similarity(area, landmark)
                if similarity > 0.7:
                    scores.append(similarity * 100)

        # 2. 通勤匹配 (AI独有)
        if commute_location:
            # 检查房源商圈是否包含通勤地点
            for district in property_business_districts:
                if commute_location in district:
                    scores.append(95.0)
                    break

                # 语义相似度
                similarity = self.ai_extraction.semantic_similarity(commute_location, district)
                if similarity > 0.7:
                    # 通勤匹配很重要,给高分
                    scores.append(similarity * 90)

        return max(scores) if scores else 50.0

    def _calculate_tag_score_weighted(
        self,
        must_have: list[str],
        nice_to_have: list[str],
        property_facilities: list[str],
    ) -> float:
        """标签匹配分数 (分优先级)."""
        if not must_have and not nice_to_have:
            return 70.0

        # 必须项匹配 (权重2x)
        must_have_matched = 0.0
        for tag in must_have:
            # 精确匹配
            if tag in property_facilities:
                must_have_matched += 1
                continue

            # 语义匹配
            for facility in property_facilities:
                similarity = self.ai_extraction.semantic_similarity(tag, facility)
                if similarity > 0.8:
                    must_have_matched += similarity
                    break

        # 加分项匹配 (权重1x)
        nice_to_have_matched = 0.0
        for tag in nice_to_have:
            if tag in property_facilities:
                nice_to_have_matched += 1
                continue

            for facility in property_facilities:
                similarity = self.ai_extraction.semantic_similarity(tag, facility)
                if similarity > 0.8:
                    nice_to_have_matched += similarity
                    break

        # 计算分数
        total_required = len(must_have) * 2 + len(nice_to_have)
        total_matched = must_have_matched * 2 + nice_to_have_matched

        if total_required == 0:
            return 70.0

        score = (total_matched / total_required) * 100

        # 必须项未全部满足 → 严重扣分
        if must_have and must_have_matched < len(must_have):
            score *= 0.7

        return min(100.0, score)

    def _calculate_commute_score(
        self,
        lead_req: LeadRequirements,
        prop_features: PropertyFeatures,
    ) -> float:
        """通勤匹配分数 (AI独有能力)."""
        if not lead_req.commute_location:
            return 0.0

        # 检查房源是否靠近通勤地点
        for district in prop_features.nearby_business_districts:
            similarity = self.ai_extraction.semantic_similarity(lead_req.commute_location, district)
            if similarity > 0.7:
                return 90.0

        return 30.0  # 通勤不便

    def _generate_match_reasons(
        self,
        lead: Lead,
        property: Property,
        lead_req: LeadRequirements,
        prop_features: PropertyFeatures,
        breakdown: MatchScoreBreakdown,
    ) -> list[str]:
        """生成匹配理由(前3条高分项)."""
        reasons = []

        if breakdown.budget_score >= 90:
            reasons.append(f"💰 预算完全匹配 ({property.rent_price}元 vs 需求{lead.budget_range})")

        if breakdown.layout_score >= 90:
            reasons.append(f"🏠 户型完全匹配 ({property.layout})")

        if breakdown.area_score >= 90:
            reasons.append(f"📍 区域高度匹配 ({property.community_name})")

        if breakdown.tag_score >= 70:
            matched_tags = set(lead_req.must_have_tags) & set(prop_features.facilities)
            if matched_tags:
                reasons.append(f"✅ 包含需求标签: {', '.join(list(matched_tags)[:3])}")

        if breakdown.commute_score >= 80:
            reasons.append(f"🚇 通勤便利 (靠近{lead_req.commute_location})")

        return reasons[:3]

    def _generate_mismatch_reasons(
        self,
        lead: Lead,
        property: Property,
        lead_req: LeadRequirements,
        prop_features: PropertyFeatures,
        breakdown: MatchScoreBreakdown,
    ) -> list[str]:
        """生成不匹配理由."""
        reasons = []

        if breakdown.budget_score < 50:
            reasons.append(f"⚠️ 价格超出预算 ({property.rent_price}元 vs {lead.budget_range})")

        if breakdown.layout_score < 50 and lead_req.layout:
            reasons.append(f"⚠️ 户型不匹配 ({property.layout} vs 需求{lead_req.layout})")

        return reasons

    def _generate_ai_insights(
        self,
        lead_req: LeadRequirements,
        prop_features: PropertyFeatures,
    ) -> list[str]:
        """生成AI洞察."""
        insights = []

        # 通勤洞察
        if lead_req.commute_location and prop_features.nearby_business_districts:
            for district in prop_features.nearby_business_districts:
                if lead_req.commute_location in district:
                    insights.append(f"🚇 通勤便利: 靠近{lead_req.commute_location}")

        # 适合人群洞察
        if "上班族" in prop_features.suitable_for and lead_req.commute_location:
            insights.append("💼 适合上班族: 周边配套完善")

        # 紧急度洞察
        if lead_req.urgency == "urgent":
            insights.append("⚡ 建议优先推荐: 客户需求紧急")

        return insights
